package com.yuntsg.ruiijn.databse_search.service;

import cn.hutool.core.img.ImgUtil;
import cn.hutool.http.HttpUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.KnnSearchResponse;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.*;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.util.ApiTypeHelper;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yuntsg.ruiijn.database_interior.service.FileDetailInteriorService;
import com.yuntsg.ruiijn.database_interior.service.FileRecordInteriorService;
import com.yuntsg.ruiijn.databse_search.entity.NcbiPdfToJpg;
import com.yuntsg.ruiijn.paperana.utils.esutils.EsServerEntity;
import com.yuntsg.ruiijn.paperana.utils.esutils.IndexConfig;
import common.bean.RRException;
import common.bean.ReturnImage;
import common.util.AesEncryptUtils;
import common.util.CentralRequestUtil;
import common.util.ExtractUtil;
import common.util.MinioUtil;
import common.vo.FetchVectorVoStep;
import common.vo.PubVectorImageFetchpmidRes;
import common.vo.PubVectorImageRes;
import common.vo.PubVectorTextRes;
import jakarta.json.stream.JsonGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

@Service
@SuppressWarnings("all")

@Slf4j
public class SearchSelfService {

    private static final String PUB_TEXT_INDEXTEST = IndexConfig.SELF_TETX_INDEX;
    private static final String PUB_IMAGE_INDEX = "picture_vector_prod";
    private static final String SELF_IMAGE_INDEX = "picture_vector_self";
    @Autowired
    private ElasticsearchClient esClient;
    @Autowired
    private BussService bussService;
    @Autowired
    private CentralRequestUtil requestUtil;
    @Autowired
    private FeatureService featureService;
    @Autowired
    private FileRecordInteriorService fileRecordService;
    @Autowired
    private FileDetailInteriorService fileDetailService;
    @Autowired
    private MinioUtil minioUtil;
    @Autowired
    private ExtractUtil extractUtil;
    @Value("${central.api.url}")
    public String escenterUrl;

    /**
     * 公共库文字检索
     *
     * @param topK         返回条数
     * @param searchVector
     * @return
     * @throws IOException
     */
    public KnnSearchResponse<PubVectorTextRes> pubSearchText(Integer topK, List<Double> searchVector) throws IOException {
        KnnSearchResponse<PubVectorTextRes> response = esClient.knnSearch(builder -> builder.index(IndexConfig.PUB_TEXT_INDEX)
                .fields("table_id", "table_type", "table_no")
                .filter(q -> q.term(t -> t.field("year").value(2022)))
                .knn(s -> s.field("feature").queryVector(searchVector).k(topK).numCandidates(100L)), PubVectorTextRes.class);
        TotalHits total = response.hits().total();
        boolean isExactResult = total.relation() == TotalHitsRelation.Eq;

        if (isExactResult) {
            log.info("There are " + total.value() + " results");
            return response;
        } else {
            log.info("There are more than " + total.value() + " results");
        }
        throw new RRException("检索出错！");
    }


    /**
     * 公共库向量检索
     *
     * @param topK         返回条数
     * @param searchVector
     * @return
     * @throws IOException
     */

    @Cacheable(cacheNames = "imgResPub")
    public FetchVectorVoStep pubSearchImage(Integer topK, List<Double> searchVector, Integer source, String pmut) {
        return requestUtil.pubSearchImage(topK, searchVector, source, pmut);
    }

    @Cacheable(cacheNames = "imgResPubForWard")
    public FetchVectorVoStep pubSearchImage(Integer topK, List<Double> searchVector, List<String> rids, String pmut) {
        return requestUtil.pubSearchImage(topK, searchVector, rids, pmut);
    }


    /**
     * 公共库向量检索
     *
     * @param topK         返回条数
     * @param searchVector
     * @return
     * @throws IOException
     */

    public List<ReturnImage> fetchInfoWithPM(String pm) throws IOException {
        SearchResponse<PubVectorImageRes> response = esClient.search(s -> s.index(PUB_IMAGE_INDEX)
                .size(200)
                .fields(Arrays.asList(FieldAndFormat.of(builder -> builder.field("table_no")),
                        FieldAndFormat.of(builder -> builder.field("serial_id")),
                        FieldAndFormat.of(builder -> builder.field("pm")),
                        FieldAndFormat.of(builder -> builder.field("img_name")),
                        FieldAndFormat.of(builder -> builder.field("top_x")),
                        FieldAndFormat.of(builder -> builder.field("top_y")),
                        FieldAndFormat.of(builder -> builder.field("bottom_x")),
                        FieldAndFormat.of(builder -> builder.field("bottom_y"))))
                .query(q -> q.term(t -> t.field("pm").value(pm))), PubVectorImageRes.class);
        TotalHits total = response.hits().total();
        boolean isExactResult = total.relation() == TotalHitsRelation.Eq;
        if (isExactResult) {
            log.info("There are " + total.value() + " results");
            List<ReturnImage> returnImages = new ArrayList<>();

            for (Hit<PubVectorImageRes> hit : response.hits().hits()) {
                PubVectorImageRes data = hit.source();
                Integer table_no = data.getTable_no();
                Integer serial_id = data.getSerial_id();
                Integer top_x = data.getTop_x();
                Integer top_y = data.getTop_y();
                Integer bottom_x = data.getBottom_x();
                Integer bottom_y = data.getBottom_y();

//                String table_type = data.get("table_type").to(String[].class)[0];
                String tableMark = "ncbi_pdf_to_jpg_" + table_no;
                NcbiPdfToJpg imageData;
                try {
                    imageData = bussService.getImgData(serial_id, tableMark);
                } catch (Exception e) {
                    continue;
                }
                ReturnImage returnImage = new ReturnImage();
                returnImage.setTableNo(table_no);
                returnImage.setSerialId(serial_id);
                returnImage.setScore(hit.score().toString());
                Image image = extractUtil.byteArrayToImage(imageData.getFigureZip());
                if (null == image) {
                    continue;
                }
                Image cut = ImgUtil.cut(image, new Rectangle(top_x, top_y, bottom_x - top_x, bottom_y - top_y));
                returnImage.setImage(ImgUtil.toBytes(cut, ImgUtil.IMAGE_TYPE_JPG));
                returnImage.setType(2);
                returnImage.setTopX(top_x);
                returnImage.setTopY(top_y);
                returnImage.setBottomX(bottom_x);
                returnImage.setBottomY(bottom_y);
                returnImage.setImageName(imageData.getFileName());
                returnImage.setPmid(AesEncryptUtils.encrypt(imageData.getPmidUt()));
                returnImages.add(returnImage);
            }
            return returnImages;
        } else {
            log.info("There are more than " + total.value() + " results");
        }
        throw new RRException("检索出错！");
    }


    /**
     * 公共库 文字 分词检索
     *
     * @param topK
     * @param searchVector
     * @return
     * @throws IOException
     */
    // @Cacheable(cacheNames = "textRes")
    public List<EsServerEntity> pubParticiplesRetrieval(String content, Integer source, Integer page) throws IOException {
        List<EsServerEntity> res = new ArrayList<>();
        Map<String, Object> param = new HashMap<>();
        param.put("type", 1);
        param.put("source", source);
        param.put("page", page);
        param.put("content", content);
        String post = HttpUtil.post(escenterUrl + "web/pub/getes", param);
        Map parse = JSONObject.parseObject(post, Map.class);
        if (parse.get("code").toString().equals("0")) {
            List<EsServerEntity> data = JSONArray.parseArray(parse.get("data").toString(), EsServerEntity.class);
            res = data;
            return res;
        } else {
            return res;
        }


//        if (source == 1) {
//            SearchResponse<JSONObject> response = esClient.search(s -> s
//                            .index(PUB_TEXT_INDEX)
//                            .fields(Arrays.asList(FieldAndFormat.of(builder -> builder.field("file_id")),
//                                    FieldAndFormat.of(builder -> builder.field("sentence")),
//                                    FieldAndFormat.of(builder -> builder.field("database_type")),
//                                    FieldAndFormat.of(builder -> builder.field("str")),
//                                    FieldAndFormat.of(builder -> builder.field("sub_project")),
//                                    FieldAndFormat.of(builder -> builder.field("table_id"))))
//                            .from((page - 1) * 5)
//                            .size(5)
//                            .highlight(h -> h
//                                    .fields("str", HighlightField.of(j -> j.type(HighlighterType.Unified)
//                                            .fragmentSize(10)
//                                            .numberOfFragments(0)))
//                            )
//                            .query(q -> q.multiMatch(t -> t
//                                            .slop(3)
//                                            .fields(Arrays.asList("str"))
//                                            .query(content)
//                                            .minimumShouldMatch("80%")
//                                    )
//                            ),
//                    JSONObject.class
//            );
//            return response.hits().hits();
//        } else {
//            SearchResponse<JSONObject> response = esClient.search(s -> s
//                            .index(PUB_TEXT_INDEX)
//                            .fields(Arrays.asList(FieldAndFormat.of(builder -> builder.field("file_id")),
//                                    FieldAndFormat.of(builder -> builder.field("paragraph_name")),
//                                    FieldAndFormat.of(builder -> builder.field("sentence")),
//                                    FieldAndFormat.of(builder -> builder.field("database_type")),
//                                    FieldAndFormat.of(builder -> builder.field("str")),
//                                    FieldAndFormat.of(builder -> builder.field("sub_project")),
//                                    FieldAndFormat.of(builder -> builder.field("table_id"))))
//                            .from((page - 1) * 5)
//                            .size(5)
//                            .highlight(h -> h
//                                    .fields("str", HighlightField.of(j -> j.type(HighlighterType.Unified)
//                                            .fragmentSize(10)
//                                            .numberOfFragments(0)))
//                            )
//                            .query(q -> q.matchPhrase
//                                    (t -> t
//                                            .slop(3)
//                                            .field("str")
//                                            .query(content)
//
//                                    )
//                            ),
//                    JSONObject.class
//            );

//            return response.hits().hits();
//        }

    }

    /**
     * 自建库 文字 分词检索
     *
     * @param topK
     * @param searchVector
     * @return
     * @throws IOException
     */
    // @Cacheable(cacheNames = "ManaltextRes")
    public List<Hit<JSONObject>> manParticiplesRetrieval(String content, Integer source, Integer page, Integer fid, Integer uid) throws IOException {
        BoolQuery.Builder allQueryBuilder = new BoolQuery.Builder();
        Query fidsQuery = TermQuery.of(t -> t.field("file_id").value(fid))._toQuery();
        Query tidsQuery = TermQuery.of(t -> t.field("table_id").value(uid))._toQuery();
        List<Query> ls = new ArrayList<>();
        ls.add(fidsQuery);
        ls.add(tidsQuery);
        allQueryBuilder.must(ls);
        Query query = MatchQuery.of(t -> t.field("str").query(content).minimumShouldMatch("45%"))._toQuery();
        allQueryBuilder.must(query);
        SearchResponse<JSONObject> response = esClient.search(s -> s
                        .index(PUB_TEXT_INDEXTEST)
                        .fields(Arrays.asList(FieldAndFormat.of(builder -> builder.field("file_id")),
                                FieldAndFormat.of(builder -> builder.field("paragraph_name")),
                                FieldAndFormat.of(builder -> builder.field("sentence")),
                                FieldAndFormat.of(builder -> builder.field("database_type")),
                                FieldAndFormat.of(builder -> builder.field("str")),
                                FieldAndFormat.of(builder -> builder.field("sub_project")),
                                FieldAndFormat.of(builder -> builder.field("table_id"))))
                        .from((page - 1) * 5)
                        .size(5)
                        .highlight(h -> h
                                .fields("str", HighlightField.of(j -> j.type(HighlighterType.Unified)
                                        .fragmentSize(10)
                                        .numberOfFragments(0)))
                        )
//                        .query(q -> q.matchPhrase
//                                (t -> t
//                                        .slop(3)
//                                        .field("str")
//                                        .query(content)
//
//                                )
//                        )
                        .query(allQueryBuilder.build()._toQuery())
                ,
                JSONObject.class
        );
        return response.hits().hits();

    }


    /**
     * 自定义库 文字 分词检索
     *
     * @param topK
     * @param searchVector
     * @return
     * @throws IOException
     */
    // @Cacheable(cacheNames = "multextRes")
    public List<Hit<JSONObject>> mulpubParticiplesRetrieval(String content, Integer source, Integer page, Integer fid, Integer uid) throws IOException {
        BoolQuery.Builder allQueryBuilder = new BoolQuery.Builder();
        Query fidsQuery = TermQuery.of(t -> t.field("file_id").value(fid))._toQuery();
        Query tidsQuery = TermQuery.of(t -> t.field("table_id").value(uid))._toQuery();
        List<Query> ls = new ArrayList<>();
        ls.add(fidsQuery);
        ls.add(tidsQuery);
        allQueryBuilder.must(ls);
        Query str = MatchQuery.of(t -> t.field("str").query(content).minimumShouldMatch("45%"))._toQuery();
        allQueryBuilder.must(str);

        if (source == 1) {
            SearchResponse<JSONObject> response = esClient.search(s -> s
                            .index(PUB_TEXT_INDEXTEST)
                            .fields(Arrays.asList(FieldAndFormat.of(builder -> builder.field("file_id")),
                                    FieldAndFormat.of(builder -> builder.field("sentence")),
                                    FieldAndFormat.of(builder -> builder.field("database_type")),
                                    FieldAndFormat.of(builder -> builder.field("str")),
                                    FieldAndFormat.of(builder -> builder.field("sub_project")),
                                    FieldAndFormat.of(builder -> builder.field("table_id"))))
                            .from((page - 1) * 5)
                            .size(5)
                            .highlight(h -> h
                                    .fields("str", HighlightField.of(j -> j.type(HighlighterType.Unified)
                                            .fragmentSize(10)
                                            .numberOfFragments(0)))
                            )
                            .query(allQueryBuilder.build()._toQuery()
                            )
//                            .query(q -> q.multiMatch(t -> t
//                                            .slop(3)
//                                            .fields(Arrays.asList("str"))
//                                            .query(content)
//                                            .minimumShouldMatch("80%")
//                                    )
//                            )
                    ,
                    JSONObject.class
            );
            return response.hits().hits();
        } else {
            SearchResponse<JSONObject> response = esClient.search(s -> s
                            .index(PUB_TEXT_INDEXTEST)
                            .fields(Arrays.asList(FieldAndFormat.of(builder -> builder.field("file_id")),
                                    FieldAndFormat.of(builder -> builder.field("paragraph_name")),
                                    FieldAndFormat.of(builder -> builder.field("sentence")),
                                    FieldAndFormat.of(builder -> builder.field("database_type")),
                                    FieldAndFormat.of(builder -> builder.field("str")),
                                    FieldAndFormat.of(builder -> builder.field("sub_project")),
                                    FieldAndFormat.of(builder -> builder.field("table_id"))))
                            .from((page - 1) * 5)
                            .size(5)
                            .highlight(h -> h
                                    .fields("str", HighlightField.of(j -> j.type(HighlighterType.Unified)
                                            .fragmentSize(10)
                                            .numberOfFragments(0)))
                            )
//                            .query(q -> q.matchPhrase
//                                    (t -> t
//                                            .slop(3)
//                                            .field("str")
//                                            .query(content)
//
//                                    )
//
//                            )
                            .query(allQueryBuilder.build()._toQuery()
                            )
                    ,
                    JSONObject.class
            );
            return response.hits().hits();
        }

    }

    public String fetchPreOrNext(String dataType, Integer sentence, String subProject, String file_id, String table_no) throws IOException {


        Map<String, Object> param = new HashMap<>();
        param.put("dataType", dataType);
        param.put("sentence", sentence);
        param.put("subProject", subProject);
        param.put("file_id", file_id);
        param.put("table_no", table_no);
        String post = HttpUtil.post(escenterUrl + "web/pub/getesprenex", param);
        Map parse = JSONObject.parseObject(post, Map.class);
        if (parse.get("code").toString().equals("0")) {
            return parse.get("data").toString();
        } else {
            return "";
        }

    }


    public void printEsBySearchRequest(SearchRequest searchRequest) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        JsonpMapper mapper = new JacksonJsonpMapper();
        JsonGenerator generator = mapper.jsonProvider().createGenerator(byteArrayOutputStream);
        mapper.serialize(searchRequest, generator);
        generator.close();
        System.out.println("打印的DSL___");
        System.out.println(byteArrayOutputStream.toString());
    }


    //  @Cacheable(cacheNames = "searchForReport")
    public List<Hit<JSONObject>> searchForReport(String content) throws IOException {
        SearchResponse<JSONObject> response = esClient
                .search(s -> s
                                .index(IndexConfig.PUB_TEXT_INDEX)
                                .fields(Arrays.asList(FieldAndFormat.of(builder -> builder.field("file_id")),
                                        FieldAndFormat.of(builder -> builder.field("sentence")),
                                        FieldAndFormat.of(builder -> builder.field("database_type")),
                                        FieldAndFormat.of(builder -> builder.field("str")),
                                        FieldAndFormat.of(builder -> builder.field("sub_project")),
                                        FieldAndFormat.of(builder -> builder.field("table_id"))))
                                .highlight(h -> h
                                        .fields("str", HighlightField.of(j -> j.type(HighlighterType.Unified)
                                                .fragmentSize(10)
                                                .numberOfFragments(0)))
                                )
                                .size(20)
                                .query(q -> q.multiMatch(t -> t
                                                .slop(3)
                                                .fields(Arrays.asList("str"))
                                                .query(content)
                                                .minimumShouldMatch("80%")
                                        )
                                ),
                        JSONObject.class
                );
        return response.hits().hits();
    }

    // @Cacheable(cacheNames = "searchForPicReport")
    public KnnSearchResponse<PubVectorImageRes> searchForPicReport(String path) throws IOException {
        ApiTypeHelper.DANGEROUS_disableRequiredPropertiesCheck(true);
        List<Double> searchVector = featureService.imageFeature(path).stream().map(Float::doubleValue).collect(Collectors.toList());
        KnnSearchResponse<PubVectorImageRes> response = esClient.knnSearch(builder -> builder.index(PUB_IMAGE_INDEX)
                .fields("table_id", "img_name", "table_no", "pmid_ut", "serial_no")
                .knn(s -> s.field("feature").queryVector(searchVector).k(10).numCandidates(10000L)), PubVectorImageRes.class);
        TotalHits total = response.hits().total();
        ApiTypeHelper.DANGEROUS_disableRequiredPropertiesCheck(false);
        boolean isExactResult = total.relation() == TotalHitsRelation.Eq;

        if (isExactResult) {
            log.info("There are " + total.value() + " results");
            return response;
        } else {
            log.info("There are more than " + total.value() + " results");
        }
        throw new RRException("检索出错！");
    }

    // @Cacheable(cacheNames = "fetchPmids")
    public KnnSearchResponse<PubVectorImageFetchpmidRes> fetchPmids(List<Double> searchVector) throws IOException {
        ApiTypeHelper.DANGEROUS_disableRequiredPropertiesCheck(true);
        KnnSearchResponse<PubVectorImageFetchpmidRes> response = esClient.knnSearch(builder -> builder.index(PUB_IMAGE_INDEX)
                .knn(s -> s.field("vector").queryVector(searchVector).k(20).numCandidates(20L)), PubVectorImageFetchpmidRes.class);
        TotalHits total = response.hits().total();
        boolean isExactResult = total.relation() == TotalHitsRelation.Eq;
        ApiTypeHelper.DANGEROUS_disableRequiredPropertiesCheck(false);
        if (isExactResult) {
            log.info("There are " + total.value() + " results");
            return response;
        } else {
            log.info("There are more than " + total.value() + " results");
        }
        throw new RRException("检索出错！");
    }


}

















